import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import threading
from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import re
import os
from datetime import datetime

# Глобальные переменные
successful_attempts = 0
failed_attempts = 0
processing_thread = None  # Для отслеживания потока обработки

# Переменная для управления паузой
pause_event = threading.Event()
pause_event.set()  # Изначально не приостановлено

# Флаг для отслеживания состояния обработки
is_processing = False

# Пути к файлам
tokens_file = r"C:\Users\1\Desktop\VS 2022\Получение токенов ВК\tokens.txt"
failed_logins_file = r"C:\Users\1\Desktop\VS 2022\Получение токенов ВК\неуспешные логи.txt"
login_pass_file = r"C:\Users\1\Desktop\VS 2022\Получение токенов ВК\log pass.txt"
driver_path = r"C:\Users\1\Desktop\edgedriver_win64\msedgedriver.exe"

def log_message(message, text_widget, msg_type="info"):
    """
    Функция для логирования сообщений в текстовом виджете с временной меткой и цветовым кодированием.
    msg_type может быть 'info', 'success' или 'error'.
    """
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_message = f"[{timestamp}] {message}\n"
    text_widget.config(state=tk.NORMAL)
    if msg_type == "info":
        text_widget.insert(tk.END, full_message, "info")
    elif msg_type == "success":
        text_widget.insert(tk.END, full_message, "success")
    elif msg_type == "error":
        text_widget.insert(tk.END, full_message, "error")
    text_widget.see(tk.END)
    text_widget.config(state=tk.DISABLED)

def append_token(token):
    """
    Добавляет токен в окно успешных токенов без временной метки.
    """
    successful_tokens_text.config(state=tk.NORMAL)
    successful_tokens_text.insert(tk.END, token + "\n")
    successful_tokens_text.config(state=tk.DISABLED)

def append_failed_login(login, password):
    """
    Добавляет неуспешный логин и пароль в соответствующее окно без временной метки.
    """
    failed_logins_text.config(state=tk.NORMAL)
    failed_logins_text.insert(tk.END, f"{login}:{password}\n")
    failed_logins_text.config(state=tk.DISABLED)

def update_status_labels():
    total_accounts = len(read_login_passwords())
    remaining = total_accounts - (successful_attempts + failed_attempts)
    success_label.config(text=f"Успешные попытки: {successful_attempts}")
    failure_label.config(text=f"Неуспешные попытки: {failed_attempts}")
    count_label.config(text=f"Всего аккаунтов: {total_accounts}")
    remaining_label.config(text=f"Оставшиеся аккаунты: {remaining}")

def save_token_to_file(token, is_error=False):
    try:
        with open(tokens_file, "a", encoding='utf-8') as file:
            if is_error:
                file.write("ошибка\n")  # Записываем "ошибка" в файл, если это ошибка
            else:
                file.write(token + "\n")  # Если токен успешно получен
        log_message(f"{'Ошибка' if is_error else 'Токен'} сохранён в файле: {tokens_file}", log_text, msg_type="info")
        if not is_error:
            append_token(token)  # Добавление токена в окно успешных токенов без временной метки
    except Exception as e:
        log_message(f"Ошибка при сохранении токена: {e}", log_text, msg_type="error")

def save_failed_login(login, password):
    try:
        with open(failed_logins_file, "a", encoding='utf-8') as file:
            file.write(f"{login}:{password}\n")
        log_message(f"Неуспешный логин сохранён в файле: {failed_logins_file}", log_text, msg_type="info")
        append_failed_login(login, password)  # Добавление логина и пароля в окно неуспешных попыток без временной метки
    except Exception as e:
        log_message(f"Ошибка при сохранении неуспешного логина: {e}", log_text, msg_type="error")

def read_login_passwords():
    if not os.path.exists(login_pass_file):
        log_message(f"Файл с логинами и паролями не найден: {login_pass_file}", log_text, msg_type="error")
        return []
    with open(login_pass_file, "r", encoding='utf-8') as file:
        accounts = [line.strip().split(":") for line in file if line.strip()]
    return accounts

def open_browser(login, password):
    global successful_attempts, failed_attempts

    log_message(f"Открытие браузера для аккаунта: {login}", log_text, msg_type="info")
    options = Options()
    options.add_argument("--inprivate")
    
    # Проверка состояния флажка для скрытия браузера
    if hide_browser_var.get():
        options.add_argument("--headless")
        options.add_argument("--disable-gpu")  # Опционально, для стабильности в headless режиме
        log_message("Браузер запускается в скрытом режиме (headless).", log_text, msg_type="info")

    service = Service(driver_path)
    driver = webdriver.Edge(service=service, options=options)

    driver.get("https://vkhost.github.io/")
    log_message("Страница https://vkhost.github.io/ открыта.", log_text, msg_type="info")

    try:
        # Нажатие на кнопку авторизации
        button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.btn[onclick='auth(6121396)']"))
        )
        button.click()
        log_message("Кнопка авторизации нажата.", log_text, msg_type="info")

        # Ожидание открытия новой вкладки
        WebDriverWait(driver, 15).until(lambda d: len(d.window_handles) > 1)
        driver.switch_to.window(driver.window_handles[-1])  # Переход на последнюю открытую вкладку
        log_message(f"Переключено на новую вкладку с URL: {driver.current_url}", log_text, msg_type="info")

        # Проверка текущего URL и ожидание элементов для авторизации
        WebDriverWait(driver, 15).until(
            lambda d: d.current_url.startswith("https://oauth.vk.com/authorize?")
        )
        log_message(f"Страница авторизации ВКонтакте открыта: {driver.current_url}", log_text, msg_type="info")

        # Заполнение логина и пароля
        email_field = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='text'][name='email']"))
        )
        password_field = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='password'][name='pass']"))
        )

        # Заполняем логин и пароль
        email_field.send_keys(login)
        log_message("Логин введён.", log_text, msg_type="info")

        password_field.send_keys(password)
        log_message("Пароль введён.", log_text, msg_type="info")

        # Нажимаем кнопку входа
        submit_button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.flat_button.oauth_button.button_wide[type='submit']"))
        )
        submit_button.click()
        log_message("Кнопка 'Войти' нажата.", log_text, msg_type="info")

        # Ожидание загрузки следующей страницы с кнопкой подтверждения
        allow_button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.flat_button[onclick='return allow(this);']"))
        )
        allow_button.click()
        log_message("Кнопка подтверждения нажата.", log_text, msg_type="info")

        # Ожидание загрузки страницы с токеном
        WebDriverWait(driver, 15).until(
            lambda d: d.current_url.startswith("https://oauth.vk.com/blank.html#access_token=")
        )
        log_message(f"Страница с токеном загружена: {driver.current_url}", log_text, msg_type="info")

        # Извлечение токена из URL
        url = driver.current_url
        token_match = re.search(r"access_token=([^&]+)&expires_in", url)
        if token_match:
            token = token_match.group(1)
            save_token_to_file(token)  # Сохраняем токен
            successful_attempts += 1
            log_message("Токен успешно получен.", log_text, msg_type="success")
        else:
            failed_attempts += 1
            save_token_to_file("", is_error=True)  # Сохраняем ошибку вместо токена
            save_failed_login(login, password)
            log_message("Ошибка: Токен не найден в URL.", log_text, msg_type="error")
            append_token("ошибка")  # Добавление "ошибка" в окно успешных токенов без временной метки

    except TimeoutException:
        failed_attempts += 1
        save_token_to_file("", is_error=True)  # Сохраняем ошибку
        save_failed_login(login, password)
        log_message(f"Ошибка: Превышено время ожидания элемента на странице. Текущий URL: {driver.current_url}", log_text, msg_type="error")
        append_token("ошибка")
    except NoSuchElementException:
        failed_attempts += 1
        save_token_to_file("", is_error=True)  # Сохраняем ошибку
        save_failed_login(login, password)
        log_message("Ошибка: Элемент не найден на странице.", log_text, msg_type="error")
        append_token("ошибка")
    except Exception as e:
        failed_attempts += 1
        save_token_to_file("", is_error=True)  # Сохраняем ошибку
        save_failed_login(login, password)
        log_message(f"Неожиданная ошибка: {e}", log_text, msg_type="error")
        append_token("ошибка")
    finally:
        driver.quit()
        log_message("Браузер закрыт.", log_text, msg_type="info")
        update_status_labels()

def process_all_accounts():
    global is_processing
    accounts = read_login_passwords()
    for login, password in accounts:
        pause_event.wait()  # Ждём, пока не будет возобновления
        if not is_processing:
            # Если процесс был остановлен до завершения, выходим
            break
        log_message(f"Обработка аккаунта: {login}", log_text, msg_type="info")
        open_browser(login, password)
    # После завершения обработки
    is_processing = False
    # Обновление кнопок
    start_button.config(state=tk.NORMAL)
    pause_button.config(state=tk.DISABLED)
    resume_button.config(state=tk.DISABLED)
    restart_button.config(state=tk.DISABLED)
    log_message("Обработка всех аккаунтов завершена.", log_text, msg_type="info")

def start_browser_thread():
    global is_processing, processing_thread
    if is_processing:
        messagebox.showinfo("Информация", "Процесс уже запущен.")
        return
    is_processing = True
    pause_event.set()  # Убедимся, что не приостановлено
    processing_thread = threading.Thread(target=process_all_accounts, daemon=True)
    processing_thread.start()
    # Обновление кнопок
    start_button.config(state=tk.DISABLED)
    pause_button.config(state=tk.NORMAL)
    resume_button.config(state=tk.DISABLED)
    restart_button.config(state=tk.DISABLED)

def pause_processing():
    global pause_button, resume_button, is_processing
    if not is_processing:
        return
    pause_event.clear()
    log_message("Процесс приостановлен.", log_text, msg_type="info")
    pause_button.config(state=tk.DISABLED)
    resume_button.config(state=tk.NORMAL)
    restart_button.config(state=tk.NORMAL)  # Активируем кнопку "Перезапустить"

def resume_processing():
    global pause_button, resume_button, is_processing
    if not is_processing:
        return
    pause_event.set()
    log_message("Процесс восстановлен.", log_text, msg_type="info")
    pause_button.config(state=tk.NORMAL)
    resume_button.config(state=tk.DISABLED)
    restart_button.config(state=tk.DISABLED)  # Деактивируем кнопку "Перезапустить"

def restart_processing():
    global is_processing, processing_thread, successful_attempts, failed_attempts
    try:
        if processing_thread and processing_thread.is_alive():
            is_processing = False  # Сигнализируем потоку завершиться
            pause_event.set()  # В случае если поток приостановлен
            processing_thread.join()  # Ждём завершения потока
        # Сброс счетчиков
        successful_attempts = 0
        failed_attempts = 0
        update_status_labels()
        log_message("Счётчики успешно сброшены.", log_text, msg_type="info")
        # Запуск процесса заново
        is_processing = True
        pause_event.set()
        processing_thread = threading.Thread(target=process_all_accounts, daemon=True)
        processing_thread.start()
        # Обновление кнопок
        start_button.config(state=tk.DISABLED)
        pause_button.config(state=tk.NORMAL)
        resume_button.config(state=tk.DISABLED)
        restart_button.config(state=tk.DISABLED)
        log_message("Процесс перезапущен с первого аккаунта.", log_text, msg_type="info")
    except Exception as e:
        messagebox.showerror("Ошибка", f"Не удалось перезапустить процесс: {e}")
        log_message(f"Ошибка при перезапуске процесса: {e}", log_text, msg_type="error")

def copy_successful_tokens():
    content = successful_tokens_text.get("1.0", tk.END).strip()
    if not content:
        messagebox.showinfo("Информация", "Нет токенов для копирования.")
        return
    root.clipboard_clear()
    root.clipboard_append(content)
    log_message("Токены скопированы в буфер обмена.", log_text, msg_type="info")

def copy_failed_logins():
    content = failed_logins_text.get("1.0", tk.END).strip()
    if not content:
        messagebox.showinfo("Информация", "Нет неуспешных логинов для копирования.")
        return
    root.clipboard_clear()
    root.clipboard_append(content)
    log_message("Неуспешные логины и пароли скопированы в буфер обмена.", log_text, msg_type="info")

def copy_selection():
    try:
        selected_text = root.focus_get().get(tk.SEL_FIRST, tk.SEL_LAST)
        root.clipboard_clear()
        root.clipboard_append(selected_text)
    except tk.TclError:
        pass  # Нет выделенного текста

def select_all(text_widget):
    text_widget.tag_add(tk.SEL, "1.0", tk.END)
    text_widget.mark_set(tk.INSERT, "1.0")
    text_widget.see(tk.INSERT)

def show_context_menu(event):
    widget = event.widget
    if isinstance(widget, tk.Text):
        context_menu.post(event.x_root, event.y_root)

def open_tokens_window():
    if hasattr(open_tokens_window, 'window') and open_tokens_window.window.winfo_exists():
        open_tokens_window.window.focus()
        return

    tokens_window = tk.Toplevel(root)
    tokens_window.title("Успешные токены")
    tokens_window.geometry("600x400")

    frame = ttk.Frame(tokens_window, padding="10")
    frame.pack(fill=tk.BOTH, expand=True)

    tokens_text = tk.Text(frame, height=20, width=70, state=tk.DISABLED, wrap=tk.WORD)
    tokens_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=tokens_text.yview)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    tokens_text.config(yscrollcommand=scrollbar.set)

    def load_tokens():
        tokens_text.config(state=tk.NORMAL)
        tokens_text.delete("1.0", tk.END)
        if os.path.exists(tokens_file):
            with open(tokens_file, "r", encoding='utf-8') as file:
                tokens = file.read()
                tokens_text.insert(tk.END, tokens)
        else:
            tokens_text.insert(tk.END, "Файл с токенами не найден.")
        tokens_text.config(state=tk.DISABLED)

    def clear_tokens():
        if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите очистить все токены?"):
            try:
                open(tokens_file, "w", encoding='utf-8').close()
                tokens_text.config(state=tk.NORMAL)
                tokens_text.delete("1.0", tk.END)
                tokens_text.config(state=tk.DISABLED)
                log_message("Все токены очищены.", log_text, msg_type="info")
                successful_tokens_text.config(state=tk.NORMAL)
                successful_tokens_text.delete("1.0", tk.END)
                successful_tokens_text.config(state=tk.DISABLED)
                update_status_labels()
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось очистить токены: {e}")

    load_tokens()

    button_frame = ttk.Frame(tokens_window)
    button_frame.pack(pady=5)

    clear_button = ttk.Button(button_frame, text="Очистить", command=clear_tokens)
    clear_button.pack()

    open_tokens_window.window = tokens_window  # Сохранение ссылки на окно

def open_failed_logins_window():
    if hasattr(open_failed_logins_window, 'window') and open_failed_logins_window.window.winfo_exists():
        open_failed_logins_window.window.focus()
        return

    failed_window = tk.Toplevel(root)
    failed_window.title("Неуспешные логины и пароли")
    failed_window.geometry("600x400")

    frame = ttk.Frame(failed_window, padding="10")
    frame.pack(fill=tk.BOTH, expand=True)

    failed_text = tk.Text(frame, height=20, width=70, state=tk.DISABLED, wrap=tk.WORD)
    failed_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=failed_text.yview)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    failed_text.config(yscrollcommand=scrollbar.set)

    def load_failed_logins():
        failed_text.config(state=tk.NORMAL)
        failed_text.delete("1.0", tk.END)
        if os.path.exists(failed_logins_file):
            with open(failed_logins_file, "r", encoding='utf-8') as file:
                failed_logins = file.read()
                failed_text.insert(tk.END, failed_logins)
        else:
            failed_text.insert(tk.END, "Файл с неуспешными логинами не найден.")
        failed_text.config(state=tk.DISABLED)

    def clear_failed_logins():
        if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите очистить все неуспешные логины и пароли?"):
            try:
                open(failed_logins_file, "w", encoding='utf-8').close()
                failed_text.config(state=tk.NORMAL)
                failed_text.delete("1.0", tk.END)
                failed_text.config(state=tk.DISABLED)
                log_message("Все неуспешные логины и пароли очищены.", log_text, msg_type="info")
                failed_logins_text.config(state=tk.NORMAL)
                failed_logins_text.delete("1.0", tk.END)
                failed_logins_text.config(state=tk.DISABLED)
                update_status_labels()
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось очистить неуспешные логины: {e}")

    load_failed_logins()

    button_frame = ttk.Frame(failed_window)
    button_frame.pack(pady=5)

    clear_button = ttk.Button(button_frame, text="Очистить", command=clear_failed_logins)
    clear_button.pack()

    open_failed_logins_window.window = failed_window  # Сохранение ссылки на окно

def clear_all_logs():
    if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите очистить все журналы, токены и неуспешные логины?"):
        try:
            # Очистка файлов
            open(tokens_file, "w", encoding='utf-8').close()
            open(failed_logins_file, "w", encoding='utf-8').close()
            # Очистка текстовых полей
            log_text.config(state=tk.NORMAL)
            log_text.delete("1.0", tk.END)
            log_text.config(state=tk.DISABLED)

            successful_tokens_text.config(state=tk.NORMAL)
            successful_tokens_text.delete("1.0", tk.END)
            successful_tokens_text.config(state=tk.DISABLED)

            failed_logins_text.config(state=tk.NORMAL)
            failed_logins_text.delete("1.0", tk.END)
            failed_logins_text.config(state=tk.DISABLED)

            # Обновление меток
            global successful_attempts, failed_attempts
            successful_attempts = 0
            failed_attempts = 0
            update_status_labels()

            # Обновление главного журнала
            log_message("Все журналы и данные очищены.", log_text, msg_type="info")

            # Закрытие окон токенов и неуспешных логинов, если они открыты
            if hasattr(open_tokens_window, 'window') and open_tokens_window.window.winfo_exists():
                open_tokens_window.window.destroy()
            if hasattr(open_failed_logins_window, 'window') and open_failed_logins_window.window.winfo_exists():
                open_failed_logins_window.window.destroy()

        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось очистить журналы: {e}")

# Настройка интерфейса tkinter
root = tk.Tk()
root.title("Получение токенов ВК")
root.geometry("900x700")
root.resizable(False, False)

# Основной фрейм
main_frame = ttk.Frame(root, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)

# Верхний фрейм для кнопок и флажка
control_frame = ttk.Frame(main_frame)
control_frame.pack(fill=tk.X, pady=5)

start_button = ttk.Button(control_frame, text="Запустить", command=start_browser_thread)
start_button.pack(side=tk.LEFT, padx=5)

pause_button = ttk.Button(control_frame, text="Приостановить", command=pause_processing, state=tk.DISABLED)
pause_button.pack(side=tk.LEFT, padx=5)

resume_button = ttk.Button(control_frame, text="Восстановить", command=resume_processing, state=tk.DISABLED)
resume_button.pack(side=tk.LEFT, padx=5)

# Добавление флажка для скрытия браузера
hide_browser_var = tk.BooleanVar()
hide_browser_check = ttk.Checkbutton(control_frame, text="Скрыть браузер", variable=hide_browser_var)
hide_browser_check.pack(side=tk.LEFT, padx=20)

# Добавление кнопки "Перезапустить"
restart_button = ttk.Button(control_frame, text="Перезапустить", command=restart_processing, state=tk.DISABLED)
restart_button.pack(side=tk.LEFT, padx=5)

# Добавление кнопки "Обновить метки"
update_labels_button = ttk.Button(control_frame, text="Обновить метки", command=update_status_labels)
update_labels_button.pack(side=tk.LEFT, padx=5)

clear_logs_button = ttk.Button(control_frame, text="Очистить все журналы", command=clear_all_logs)
clear_logs_button.pack(side=tk.LEFT, padx=5)

# Средний фрейм для статуса
status_frame = ttk.Frame(main_frame)
status_frame.pack(fill=tk.X, pady=5)

success_label = ttk.Label(status_frame, text="Успешные попытки: 0", foreground="green", cursor="hand2")
success_label.pack(side=tk.LEFT, padx=10)
success_label.bind("<Button-1>", lambda e: copy_successful_tokens())

failure_label = ttk.Label(status_frame, text="Неуспешные попытки: 0", foreground="red", cursor="hand2")
failure_label.pack(side=tk.LEFT, padx=10)
failure_label.bind("<Button-1>", lambda e: copy_failed_logins())

count_label = ttk.Label(status_frame, text="Всего аккаунтов: 0", foreground="blue")
count_label.pack(side=tk.LEFT, padx=10)

remaining_label = ttk.Label(status_frame, text="Оставшиеся аккаунты: 0", foreground="orange")
remaining_label.pack(side=tk.LEFT, padx=10)

# Нижний фрейм для логов и токенов
logs_frame = ttk.Frame(main_frame)
logs_frame.pack(fill=tk.BOTH, expand=True, pady=10)

# Журнал действий
log_frame = ttk.LabelFrame(logs_frame, text="Журнал действий")
log_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)

log_text = tk.Text(log_frame, height=20, width=40, state=tk.DISABLED, wrap=tk.WORD)
log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

log_scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=log_text.yview)
log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
log_text.config(yscrollcommand=log_scrollbar.set)

# Настройка тегов для цветового кодирования
log_text.tag_config("info", foreground="black")
log_text.tag_config("success", foreground="green")
log_text.tag_config("error", foreground="red")

# Успешные токены
tokens_frame = ttk.LabelFrame(logs_frame, text="Успешные токены")
tokens_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)

successful_tokens_text = tk.Text(tokens_frame, height=20, width=40, state=tk.DISABLED, wrap=tk.WORD)
successful_tokens_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

tokens_scrollbar = ttk.Scrollbar(tokens_frame, orient=tk.VERTICAL, command=successful_tokens_text.yview)
tokens_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
successful_tokens_text.config(yscrollcommand=tokens_scrollbar.set)

# Настройка тегов для цветового кодирования в успешных токенах
successful_tokens_text.tag_config("success", foreground="green")
successful_tokens_text.tag_config("error", foreground="red")

# Неуспешные логины
failed_frame = ttk.LabelFrame(main_frame, text="Неуспешные логины и пароли")
failed_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=10)

failed_logins_text = tk.Text(failed_frame, height=10, width=80, state=tk.DISABLED, wrap=tk.WORD)
failed_logins_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

failed_scrollbar = ttk.Scrollbar(failed_frame, orient=tk.VERTICAL, command=failed_logins_text.yview)
failed_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
failed_logins_text.config(yscrollcommand=failed_scrollbar.set)

failed_logins_text.tag_config("error", foreground="red")

# Инициализация логов
log_message("Журнал действий:", log_text, msg_type="info")
# Удалены начальные сообщения для successful_tokens_text и failed_logins_text

# Создание общего контекстного меню
context_menu = tk.Menu(root, tearoff=0)
context_menu.add_command(label="Копировать", command=copy_selection)
context_menu.add_command(label="Выбрать всё", command=lambda: select_all(root.focus_get()))

# Привязка контекстного меню ко всем текстовым полям
log_text.bind("<Button-3>", show_context_menu)
successful_tokens_text.bind("<Button-3>", show_context_menu)
failed_logins_text.bind("<Button-3>", show_context_menu)

# Обновляем метку количества аккаунтов при запуске приложения
update_status_labels()

# Запуск главного цикла
root.mainloop()

Добав поле для лимита, то есть, если там будет число 5, то чтобы при каждом получении 5 токенов, процесс приостановился

---

python "C:\Users\1\Desktop\VS 2022\Входящие сообщения ВК\Входящие_сообщения_ВК.pyw"

pip install vk_api